package com.xingyang.yutnori.model;

import java.util.ArrayList;
import java.util.List;

/**
 * Manages the state and rules of a Yut Nori game.
 */
public class Game {
    // Game state
    private List<Player> players;
    private int currentPlayerIndex;
    private Board board;
    private boolean gameOver;
    private Player winner;
    
    // Throw state
    private boolean hasRolled;
    private List<Integer> availableRolls;
    private int currentRoll;
    private boolean reThrowAllowed;
    
    /**
     * Constructs a new game with the specified players.
     * 
     * @param players The players participating in the game
     */
    public Game(List<Player> players) {
        if (players == null || players.size() != 2) {
            throw new IllegalArgumentException("Game requires exactly 2 players");
        }
        
        this.players = new ArrayList<>(players);  // Create a new list to maintain order
        this.board = new Board();
        this.availableRolls = new ArrayList<>();
        
        // Ensure Player 1 is first in the list
        if (!this.players.get(0).getName().contains("1")) {
            // Swap players if Player 2 is first
            Player temp = this.players.get(0);
            this.players.set(0, this.players.get(1));
            this.players.set(1, temp);
        }
        
        System.out.println("Game initialized with players: " + 
                         this.players.get(0).getName() + " (first) and " + 
                         this.players.get(1).getName());
        
        // Reset the game state
        resetGame();
    }
    
    /**
     * Resets the game to its initial state.
     */
    public void resetGame() {
        currentPlayerIndex = 0;  // Ensure Player 1 (index 0) starts first
        gameOver = false;
        winner = null;
        hasRolled = false;
        availableRolls.clear();
        currentRoll = 0;
        reThrowAllowed = false;
        
        // Reset all players' pieces to start at position -1 (home)
        for (Player player : players) {
            player.resetPieces();
        }
        
        System.out.println("Game reset - Player 1 starts first");
    }
    
    /**
     * Gets the board associated with this game.
     * 
     * @return The game board
     */
    public Board getBoard() {
        return board;
    }
    
    /**
     * Gets the list of players.
     * 
     * @return The list of players
     */
    public List<Player> getPlayers() {
        return players;
    }
    
    /**
     * Gets the current player.
     * 
     * @return The current player
     */
    public Player getCurrentPlayer() {
        return players.get(currentPlayerIndex);
    }
    
    /**
     * Gets the winner of the game.
     * 
     * @return The winning player, or null if the game is not over
     */
    public Player getWinner() {
        return winner;
    }
    
    /**
     * Checks if the game is over.
     * 
     * @return True if the game is over, false otherwise
     */
    public boolean isGameOver() {
        return gameOver;
    }
    
    /**
     * Checks if the current player has rolled the Yut sticks.
     * 
     * @return True if the current player has rolled, false otherwise
     */
    public boolean hasRolled() {
        return hasRolled;
    }
    
    /**
     * Gets the current roll result.
     * 
     * @return The current roll result
     */
    public int getCurrentRoll() {
        return currentRoll;
    }
    
    /**
     * Gets the list of available roll results that can be used.
     * 
     * @return The list of available roll results
     */
    public List<Integer> getAvailableRolls() {
        return new ArrayList<>(availableRolls);
    }
    
    /**
     * Checks if the current player is allowed to throw again.
     * 
     * @return True if the current player can throw again, false otherwise
     */
    public boolean isReThrowAllowed() {
        return reThrowAllowed;
    }
    
    /**
     * Handles the result of throwing the Yut sticks.
     */
    public void handleThrowResult(int result) {
        // Reset movement state
        hasRolled = true;
        currentRoll = result;
        availableRolls.add(result);
        
        // Set re-throw flag based on rules - only Yut(4) and Mo(5) allow re-throws
        reThrowAllowed = (result == YutSet.YUT || result == YutSet.MO);
        
        System.out.println("Throw result: " + result + " (" + YutSet.getRollName(result) + ")" +
                         "\nAvailable rolls: " + availableRolls + 
                         "\nRe-throw allowed: " + reThrowAllowed);
    }
    
    /**
     * Selects a specific roll result to use from the available options.
     * 
     * @param roll The roll result to select
     * @return True if the selection was successful, false otherwise
     */
    public boolean selectRoll(int roll) {
        if (availableRolls.contains(roll)) {
            currentRoll = roll;
            return true;
        }
        return false;
    }
    
    /**
     * Checks if a piece can be moved with the given roll.
     */
    public boolean canMove(Piece piece, int roll) {
        if (piece == null || roll == 0 || !hasRolled || availableRolls.isEmpty()) {
            return false;
        }
        
        // Make sure the piece belongs to the current player
        if (piece.getOwner() != getCurrentPlayer()) {
            return false;
        }
        
        // Make sure the roll is available
        if (!availableRolls.contains(roll)) {
            return false;
        }

        // Allow moving any piece that hasn't finished
        return piece.getPosition() < 30;
    }
    
    /**
     * Gets all pieces at a specific position on the board.
     */
    public List<Piece> getPiecesAtPosition(int position) {
        List<Piece> piecesAtPosition = new ArrayList<>();
        for (Player player : players) {
            for (Piece piece : player.getPieces()) {
                if (piece.getPosition() == position) {
                    piecesAtPosition.add(piece);
                }
            }
        }
        return piecesAtPosition;
    }
    
    /**
     * Sets whether the current player is allowed to throw again.
     */
    public void setReThrowAllowed(boolean allowed) {
        this.reThrowAllowed = allowed;
    }

    /**
     * Moves a piece with a specific path choice
     */
    public boolean movePiece(Piece piece, int roll, Board.PathType chosenPath) {
        if (!canMove(piece, roll)) {
            return false;
        }

        int currentPosition = piece.getPosition();
        int nextPosition = board.getNextPosition(currentPosition, roll, chosenPath);
        
        // Update piece position (this will also update all stacked pieces)
        piece.setPosition(nextPosition);
        
        // Handle piece stacking and capture for the entire stack
        handlePieceInteraction(piece, nextPosition);
        
        // Remove the used roll
        availableRolls.remove(Integer.valueOf(roll));
        
        return true;
    }

    /**
     * Moves a piece using the default path
     */
    public boolean movePiece(Piece piece, int roll) {
        return movePiece(piece, roll, Board.PathType.MAIN_PATH);
    }
    
    /**
     * Ends the current player's turn and advances to the next player.
     */
    public void endTurn() {
        // Reset current player's state
        hasRolled = false;
        availableRolls.clear();
        currentRoll = 0;
        reThrowAllowed = false;
        
        // Switch to next player
        currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
        
        // Log turn change for debugging
        System.out.println("\n=== Turn Change ===");
        System.out.println("Previous player finished turn");
        System.out.println("New current player: " + getCurrentPlayer().getName());
        System.out.println("Game state: " + this.toString());
        System.out.println("==================\n");
    }
    
    /**
     * Sets whether the current player has rolled the Yut sticks.
     */
    public void setHasRolled(boolean hasRolled) {
        this.hasRolled = hasRolled;
    }
    
    /**
     * Clears all available rolls.
     */
    public void clearAvailableRolls() {
        this.availableRolls.clear();
    }
    
    /**
     * Sets the current roll value.
     */
    public void setCurrentRoll(int roll) {
        this.currentRoll = roll;
    }
    
    /**
     * Finishes the current move.
     * If there are no more available rolls or re-throws, the turn is considered complete.
     */
    public void finishMove() {
        System.out.println("Finishing move for " + getCurrentPlayer().getName() + 
                         "\nAvailable rolls: " + availableRolls.size() + 
                         "\nRe-throw allowed: " + reThrowAllowed);
        
        // If there are more available rolls, the player can still move pieces
        if (!availableRolls.isEmpty()) {
            System.out.println("Player still has available rolls: " + availableRolls);
            return;
        }
        
        // If re-throw is allowed and no rolls are available, reset for next throw
        if (reThrowAllowed && availableRolls.isEmpty()) {
            hasRolled = false;  // Allow player to throw again
            System.out.println("Player can throw again");
            return;
        }
        
        // Otherwise, the turn is over
        System.out.println("Turn is over, switching to next player");
        endTurn();
    }
    
    /**
     * Checks if the game is over (a player has moved all pieces to the end).
     * 
     * @return True if the game is over, false otherwise
     */
    private boolean checkGameOver() {
        for (Player player : players) {
            if (player.hasFinished()) {
                gameOver = true;
                winner = player;
                return true;
            }
        }
        return false;
    }
    
    /**
     * Gets the Korean name for the current roll.
     * 
     * @param roll The roll to get the name for
     * @return The Korean name of the roll
     */
    public String getRollName(int roll) {
        return YutSet.getRollName(roll);
    }

    // Add debug method for testing
    public void debugState() {
        System.out.println("\nGame State Debug:" +
                         "\nCurrent Player: " + getCurrentPlayer().getName() +
                         "\nHas Rolled: " + hasRolled +
                         "\nAvailable Rolls: " + availableRolls +
                         "\nCurrent Roll: " + currentRoll +
                         "\nRe-throw Allowed: " + reThrowAllowed);
    }

    @Override
    public String toString() {
        return String.format("Game State:\nCurrent Player: %s\nHas Rolled: %b\nAvailable Rolls: %s\nCurrent Roll: %d\nRe-throw Allowed: %b",
                           getCurrentPlayer().getName(),
                           hasRolled,
                           availableRolls,
                           currentRoll,
                           reThrowAllowed);
    }

    /**
     * Handles piece interaction (stacking and capturing) at a position
     */
    private void handlePieceInteraction(Piece piece, int position) {
        // Get all pieces at the target position
        List<Piece> piecesAtPosition = new ArrayList<>();
        for (Player player : players) {
            for (Piece otherPiece : player.getPieces()) {
                if (otherPiece.getPosition() == position && !otherPiece.isStacked() && otherPiece != piece) {
                    piecesAtPosition.add(otherPiece);
                }
            }
        }

        // If there are pieces and they belong to a different player, capture them
        if (!piecesAtPosition.isEmpty()) {
            Piece targetPiece = piecesAtPosition.get(0);
            if (targetPiece.getOwner() != piece.getOwner()) {
                // Capture the pieces - send them back to home (-1)
                for (Piece capturedPiece : piecesAtPosition) {
                    capturedPiece.setPosition(-1); // Send back to home
                    // If the captured piece has stacked pieces, also send them back home
                    if (capturedPiece.hasStackedPieces()) {
                        for (Piece stackedPiece : capturedPiece.getStackedPieces()) {
                            stackedPiece.setPosition(-1);
                        }
                        capturedPiece.clearStackedPieces();
                    }
                }
                // Allow re-throw after capturing opponent's pieces
                reThrowAllowed = true;
                    } else {
                // Stack pieces of the same player
                for (Piece stackedPiece : piecesAtPosition) {
                    piece.stackPiece(stackedPiece);
                        }
                    }
        }
    }

    /**
     * Checks if a capture occurred at the given position
     */
    private boolean checkCapture(Piece piece, int position) {
        for (Player player : players) {
            if (player != piece.getOwner()) {
                for (Piece otherPiece : player.getPieces()) {
                    if (otherPiece.getPosition() == position) {
                        return true;
                }
            }
        }
        }
        return false;
    }

    /**
     * Checks if the given player is the current player
     */
    public boolean isCurrentPlayer(Player player) {
        return getCurrentPlayer() == player;
    }
} 